#pragma once
/*
 *  wait group lock as go. this is like CountDownLatch.
 */
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <functional>


namespace anet {
	namespace utils {
		class CWaitGroup;
		typedef std::shared_ptr<CWaitGroup> ShareWorkGroupPtr;

		class CWaitGroup {
		public:	
			static ShareWorkGroupPtr Create() {
				struct make_shared_enabler : CWaitGroup {};
				return std::make_shared<make_shared_enabler>();
			}

		public:
			void add(int count = 1) {
				m_counter += count;
			}

			void done() {
				m_counter--;
				m_cv.notify_all();
			}

			void wait() {
				std::unique_lock<std::mutex> l(m_mutex);
				m_cv.wait(l, [&]() {
					return m_counter <= 0;
				});
			}

			template<class Rep, class Period>
			void wait(const std::chrono::duration<Rep, Period>& timeout) {
				std::unique_lock<std::mutex> l(m_mutex);
				m_cv.wait_for(l, timeout, [&]() {
					return m_counter <= 0;
				});
			}

		private:
			CWaitGroup() : m_counter(0) {}
			virtual ~CWaitGroup() = default;

		private:
			// mutex
			std::mutex m_mutex;

			// group count
			std::atomic<int> m_counter;

			// condition variable
			std::condition_variable m_cv;
		};
	}
}
